#include <stdlib.h>
#include <windows.h>
#include <time.h>
#include <gl/gl.h>
#include <gl/glu.h>

#include "Game.hpp"
#include "Input/InputMapper.h"
#include "GameOver.hpp"
#include "Render/Camera.hpp"
#include "boost/shared_ptr.hpp"
#include "Render/MapRenderer.hpp"

void RenderCube();
void RenderWireFrameCube();
void RenderWireFrameCube();
void DrawFPS(float fpsAvg);
GLvoid glPrint( const char * fmt, ...);

GLuint gBase;
GLfloat gCnt1;
GLfloat gCnt2;

Game::Game(const boost::shared_ptr< Display > & displayPtr)
     : mFPS(new FPS()),
     mDisplayPtr(displayPtr) 
{
     srand( static_cast< unsigned int >( time( NULL ) ) );

     boost::shared_ptr< MapRenderer > mapRendererPtr( new MapRenderer() );
     mMapRendererPtr = mapRendererPtr;
}

Game::~Game()
{
     if( mFPS != NULL)
     {
          delete mFPS;
          mFPS = 0;
     }
}

void Game::StartGame()
{
     HDC device = mDisplayPtr->GetDisplayDevice();

     gBase = glGenLists(96);

     mFont = CreateFont( -24, 0, 0, 0, FW_BOLD, 
          FALSE, FALSE, FALSE, 
          ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, 
          FF_DONTCARE | DEFAULT_PITCH, "Courier New");

     mOldFont = (HFONT)SelectObject(device, mFont);
     wglUseFontBitmaps(device, 32, 96, gBase);
     SelectObject(device, mOldFont);
     DeleteObject(mFont);

     mFPS->StartCounter();
}

void Game::UpdateGame()
{
	HWND window = mDisplayPtr->GetDisplayWindow();
	bool active = mDisplayPtr->GetDisplayIsActive();

	bool gameOver = false;
	while( !gameOver ) 
	{			
		MessagePump( window, active );

          InputMapping::InputMapper::GetInstance().Dispatch();
          InputMapping::InputMapper::GetInstance().Clear();

          gameOver = GameOver::GetInstance().IsGameOver(); 

		if( !gameOver )
		{
               boost::shared_ptr< MapRenderer > test( new MapRenderer() );
               Update(0.0f, 0.0f);
               Render();
		}
	}
}

void Game::StopGame()
{
     glDeleteLists(gBase, 96);
}

void Game::MessagePump( HWND window, bool active )
{
     MSG message;
     float elapsedTime = 0.0f;
     if( active )
     {
          if( PeekMessage( &message, window, 0, 0, PM_REMOVE ) ) 
          {
               TranslateMessage( &message ); 
               DispatchMessage( &message );
          }

          elapsedTime = mFPS->PumpFrame();
     }
     else
     {
          if( GetMessage( &message, window, 0, 0 ) ) 
          {
               TranslateMessage( &message );
               DispatchMessage( &message );
          }

          elapsedTime = mFPS->PumpFrame();
     }
}

void Game::Update(float frameTime, float elapsedTime)
{

}

void Game::Render()
{
     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
     glClearColor(1.0f, 1.0f, 1.0f, 1);

     //RenderCube();
     //RenderWireFrameCube();
     //glTranslatef(0.0f, 0.0f, 6.0f);

     Camera::GetInstance().Render();
     mMapRendererPtr->RenderAll();

     glLoadIdentity();
     DrawFPS(mFPS->GetFPSAvg());

     SwapBuffers( mDisplayPtr->GetDisplayDevice() );
}

void RenderCube()
{
     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
     glBegin(GL_QUADS);

          //front
          glColor3f(1.0f, 0.0f, 0.0f); //red
          glVertex3f(-1.0f, 1.0f, 0.0f); //top-left
          glVertex3f( 1.0f, 1.0f, 0.0f); //top-right
          glVertex3f(1.0f, -1.0f, 0.0f); //bottom-right
          glVertex3f(-1.0f, -1.0f, 0.0f); //bottom-left

          //right
          glColor3f(1.0f, 0.0f, 0.0f); //green
          glVertex3f( 1.0f, 1.0f, 0.0f);
          glVertex3f(1.0f, 1.0f, -2.0f);
          glVertex3f(1.0f, -1.0f, -2.0f);
          glVertex3f(1.0f, -1.0f, 0.0f);

          //back
          glColor3f(1.0f, 0.0f, 0.0f); //yellow
          glVertex3f(-1.0f, 1.0f, -2.0f); //top-left
          glVertex3f( 1.0f, 1.0f, -2.0f); //top-right
          glVertex3f(1.0f, -1.0f, -2.0f); //bottom-right
          glVertex3f(-1.0f, -1.0f, -2.0f); //bottom-left

          //bottom
          glColor3f(1.0f, 0.0f, 0.0f); //purple
          glVertex3f(-1.0f, -1.0f, -2.0f); //top-left
          glVertex3f( 1.0f, -1.0f, -2.0f); //top-right
          glVertex3f(1.0f, -1.0f, 0.0f); //bottom-right
          glVertex3f(-1.0f, -1.0f, 0.0f); //bottom-left

          //top
          glColor3f(1.0f, 0.0f, 0.0f); //turquoise
          glVertex3f(-1.0f, 1.0f, -2.0f); //top-left
          glVertex3f( 1.0f, 1.0f, -2.0f); //top-right
          glVertex3f(1.0f, 1.0f, 0.0f); //bottom-right
          glVertex3f(-1.0f, 1.0f, 0.0f); //bottom-left

          //left
          glColor3f(1.0f, 0.0f, 0.0f); //blue
          glVertex3f(-1.0f, 1.0f, -2.0f); //top-left
          glVertex3f(-1.0f, 1.0f, 0.0f); //top-right
          glVertex3f( -1.0f, -1.0f, 0.0f); //bottom-right
          glVertex3f(-1.0f, -1.0f, -2.0f); //bottom-left
     glEnd();
}

void RenderWireFrameCube()
{
     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
     glColor3f(0.0f, 0.0f, 0.0f); //black
     glBegin(GL_QUADS);

          glVertex3f(-1.01f, 1.01f, 0.01f); //top-left
          glVertex3f( 1.01f, 1.01f, 0.01f); //top-right
          glVertex3f(1.01f, -1.01f, 0.01f); //bottom-right
          glVertex3f(-1.01f, -1.01f, 0.01f); //bottom-left

          //right
          glVertex3f( 1.01f, 1.01f, 0.01f);
          glVertex3f(1.01f, 1.01f, -2.01f);
          glVertex3f(1.01f, -1.01f, -2.01f);
          glVertex3f(1.01f, -1.01f, 0.01f);

          //back
          glVertex3f(-1.01f, 1.01f, -2.01f); //top-left
          glVertex3f( 1.01f, 1.01f, -2.01f); //top-right
          glVertex3f(1.01f, -1.01f, -2.01f); //bottom-right
          glVertex3f(-1.01f, -1.01f, -2.01f); //bottom-left

          //bottom
          glVertex3f(-1.01f, -1.01f, -2.01f); //top-left
          glVertex3f( 1.01f, -1.01f, -2.01f); //top-right
          glVertex3f(1.01f, -1.01f, 0.01f); //bottom-right
          glVertex3f(-1.01f, -1.01f, 0.01f); //bottom-left

          //top
          glVertex3f(-1.01f, 1.01f, -2.01f); //top-left
          glVertex3f( 1.01f, 1.01f, -2.01f); //top-right
          glVertex3f(1.01f, 1.01f, 0.01f); //bottom-right
          glVertex3f(-1.01f, 1.01f, 0.01f); //bottom-left

          //left
          glVertex3f(-1.01f, 1.01f, -2.01f); //top-left
          glVertex3f(-1.01f, 1.01f, 0.01f); //top-right
          glVertex3f( -1.01f, -1.01f, 0.01f); //bottom-right
          glVertex3f(-1.01f, -1.01f, -2.01f); //bottom-left
     glEnd();
}

void DrawFPS(float fpsAvg)
{
     glTranslatef(0.0f,0.0f,-4.0f);
     glColor3f(0.0f, 0.0f, 1.0f);
     glRasterPos2f( -1.5f, 1.5f);
     glPrint( "%.0f", fpsAvg );
}

GLvoid glPrint( const char * fmt, ...)
{
     char text[256];
     va_list ap;

     if( fmt == NULL)
          return;

     va_start(ap, fmt);
          vsprintf_s(text, fmt, ap);
     va_end(ap);

     glPushAttrib(GL_LIST_BIT);
     glListBase(gBase - 32);
     glCallLists(strlen(text), GL_UNSIGNED_BYTE, text);
     glPopAttrib();
}